package timezone

import (
	_ "embed"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/zh-five/xdaemon"
	configinit "gopkg.openfuyao.cn/bkecommon/cluster/initialize"
	"gopkg.openfuyao.cn/bkecommon/ntp"

	"gopkg.openfuyao.cn/bkeadm/pkg/server"
	"gopkg.openfuyao.cn/bkeadm/utils"
)

var (
	localtimePath = "/etc/localtime"
	timezone      = "Asia/Shanghai"
	timezonePath  = "/etc/timezone"
	zoneDirs      = []string{
		"/usr/share/zoneinfo",
		"/usr/lib/zoneinfo",
		"/usr/local/share/zoneinfo",
		"/system/usr/share/zoneinfo",
	}
)

func findTimezoneFile(zone string) (string, error) {
	for _, dir := range zoneDirs {
		path := filepath.Join(dir, zone)
		if _, err := os.Stat(path); err == nil {
			return path, nil
		}
	}
	return "", fmt.Errorf("timezone file not found for %s", zone)
}

func copyFile(src, dst string) error {
	data, err := os.ReadFile(src)
	if err != nil {
		return err
	}
	return os.WriteFile(dst, data, utils.DefaultFilePermission)
}

func SetTimeZone() error {
	if utils.Exists(timezonePath) {
		tz, err := os.ReadFile(timezonePath)
		if err != nil {
			return err
		}
		if string(tz) == timezone {
			return nil
		}
	}

	zoneInfoPath, err := findTimezoneFile(timezone)
	if err != nil {
		return err
	}

	if _, err = os.Stat(localtimePath); err == nil {
		if err = os.Rename(localtimePath, localtimePath+".bak"); err != nil {
			fmt.Printf("rename file err: %v", err)
		}
	}

	if err = os.Symlink(zoneInfoPath, localtimePath); err == nil {
		return os.WriteFile(timezonePath, []byte(timezone), utils.DefaultFilePermission)
	}

	if err = copyFile(zoneInfoPath, localtimePath); err != nil {
		return fmt.Errorf("sym link and copy file both fail: %v", err)
	}

	return os.WriteFile(timezonePath, []byte(timezone), utils.DefaultFilePermission)
}

func NTPServer(ntpServer, hostIP string, online bool) (string, error) {

	if ntpServer != utils.LocalNTPName {
		err := ntp.Date(ntpServer)
		if err == nil {
			return ntpServer, nil
		}
		if online {
			for i := 0; i < 2; i++ {
				time.Sleep(2 * time.Second)
				err = ntp.Date(ntpServer)
				if err == nil {
					return ntpServer, nil
				}
			}
			return "", errors.New(fmt.Sprintf("Failed to connect to the ntp server %s", ntpServer))
		}
	}

	if ntpServer != utils.LocalNTPName && ntpServer != configinit.DefaultNTPServer {
		return "", errors.New(fmt.Sprintf("ntp server %s is unreachable", ntpServer))
	}

	bin, err := utils.ExecPath()
	if err != nil {
		return "", errors.New(fmt.Sprintf("Failed to obtain the execution path %s", err.Error()))
	}

	env := os.Environ()
	env = append(env, "bke="+bin)
	cmd := &exec.Cmd{
		Path:        bin,
		Args:        []string{bin, "start", "ntpserver", "--systemd"},
		Env:         env,
		SysProcAttr: xdaemon.NewSysProcAttr(),
	}
	err = cmd.Start()
	if err != nil {
		return "", err
	}
	if !server.TryConnectNTPServer(fmt.Sprintf("127.0.0.1:%d", utils.DefaultNTPServerPort)) {
		return "", errors.New(fmt.Sprintf("Failed to connect to the ntp server %s", fmt.Sprintf("127.0.0.1:%d", utils.DefaultNTPServerPort)))
	}
	return fmt.Sprintf("%s:%d", hostIP, utils.DefaultNTPServerPort), nil
}
